﻿
#include "AState.h"
#include "AMachine.h"
#include "AString.h"
#include "AXML.h"

CAState::CAState(CAMachine* pmachine)
{
	GUARD_FUNCTION();

	//_Assert(pmachine); if Machine 
	_machine = pmachine;
	_parent = null;

    _name = "state-noname";
	_substates = new CAStates();
	_trans = CCArray::array();
	_trans->retain();

	autorelease();
}

CAState::~CAState()
{
	_trans->release();
	_trans = null;
	_substates->release();
	_substates = null;
}

int CAState::init()
{
    _Assert(null != _machine);
    return 0;
}

int CAState::deinit()
{
    _Assert(null != _machine);
    return 0;
}

CAString CAState::getFullName() const
{
    if (null != getParent())
	{
        return getParent()->getFullName() + "." + _name;
	}
    return _name;
}

CAString CAState::getFullDesc() const
{
    CAString d = _desc;
	if (d.length() <= 0) d = _name;
    if (null != _curstate)
		return d + "." + _curstate->getFullDesc();
    return d;
}

/*
void CAState::setMachine(CAMachine* p)
{ 
	_machine = p; 
	//CAState* pstate;
	//MAP_FOREACH(_substates, pstate, null, pstate->setMachine(p));
}
*/

void CAState::collectAllStates(CAStates* states)
{
	_Assert(states);

	states->setObject(this, this->getFullName());
	MAP_FOREACH(CAState*, pstate, _substates, 
	{
		pstate->collectAllStates(states);
	});
}

void CAState::addState(CAState* ss)
{
    _Assert(null != _machine);
    ss->setParent(this); 
	ss->init();
	_substates->setObject(ss, ss->getName()); //ss->getFullName()); 
	_machine->addStateIntoAllStatesTable(ss);
}

void CAState::removeState(CAState* ss) 
{
    _Assert(null != _machine);
	ss->deinit();
	_substates->removeObjectForKey(ss->getName()); //getFullName()); 
	_machine->removeStateFromAllStatesTable(ss);
}

void CAState::setCurState(CAState* ps)
{
	_Assert(ps);
	_Assert(ps == _substates->objectForKey(ps->getName()));
	_curstate = ps;
}

int CAState::getDeapth() const
{
	int d = 0;
    const CAState* p = getParent();
    while (null != p)
    {
        d++;
        p = p->getParent();
    }
    return d;
}

int CAState::Enter(ccTime time)
{
	GUARD_FUNCTION();

	_Assert(_machine);

	_timeEnter = time;
	_counter = 0;

	_machine->OnEventNotify(this, SE_StateEntering);

	_curstate = _substates->objectForKey(_initstate);
	if (_initstate.length() <= 0)
	{
		//no sub states
		_Assert(_substates->count() <= 0);
		//_curstate = null;
	}

    //_machine->DoAction(this, enter);
    if (null != _curstate)
    {
        _curstate->Enter(time);
    }
	_machine->OnEventNotify(this, SE_StateEntered);

    return 0;
}

int CAState::Leave(ccTime time)
{
	GUARD_FUNCTION();

	_Assert(null != _machine);
	_Assert(null == _parent || _parent->getCurState() == this);

	_timeLeave = time;
	_machine->OnEventNotify(this, SE_StateLeaving);

    //_machine->DoAction(this, leave);
    if (null != _curstate)
    {
        _curstate->Leave(time);
    }
	_machine->OnEventNotify(this, SE_StateLeaved);
    return 0;
}

int CAState::Update(ccTime time)
{
	_Assert(null != _machine);

	ccTime timeDeta = time - _timeEnter;
	_machine->onStateUpdate(this, _counter, timeDeta);
	_counter++;

	CAObject* t;
	CCARRAY_FOREACH(_trans, t)
	{
		CATransition* pt = (CATransition*)t;
		if (_machine->checkCondition(this, *pt))
		{
			_machine->navigateToState(time, this, pt->tostate);
			return 0;
		}
	}
    if (null != _curstate)
    {
        _curstate->Update(timeDeta);
    }
    return 0;
}

#if 0
CAState* CAState::findState(const CAString& tostate, int nBackSearchLevel)
{
	CAState* pstate;
	
	MAP_FOREACH(_substates, pstate, null, if (pstate->getFullName() == tostate) return pstate; );
	/*
	因为不同的状态的子状态是可以同名的，所以利用单纯的状态getName会出现问题
	strings subs;
	CAStringUtils::split(tostate, ".", subs);
	pstate = _substates->objectForKey(subs[subs.size() - 1]);
	if (null != pstate)
	{
		_Assert(pstate->getFullName() == tostate);
		return pstate;
	}
	*/
	if (nBackSearchLevel > 0 && getParent() != null)
	{
		return getParent()->findState(tostate, nBackSearchLevel - 1);
	}
	return null;
}
#endif


void CAState::initialize(CAXMLNode* pnodeState)
{
	GUARD_FUNCTION();

	//load attrs
	_name = pnodeState->attr("name");
	_Assert(_name.length() > 0);
	_initstate = pnodeState->attr("init");

	//sub states
	CAXMLNodes* pnodes = pnodeState->nodes("state");
	CAObject* pobj;
	CCARRAY_FOREACH(pnodes, pobj)
	{
		GUARD_FIELD(_new_state);
		
		CAXMLNode* pnode = (CAXMLNode*)pobj;
		CAState* ps = new CAState(_machine);
		//init parent first!
		//a.b.c.d.e e 在initialize过程中e会第一个被加到d中
		//然后是 d加到c中，c加到b中，b加到a中。
		//在添加过程中才设置parent，会使getFullName无法工作。
		ps->setParent(this);
		ps->initialize(pnode);
		if (_initstate.length() <= 0)
		{
			_initstate = ps->getName();
		}
		this->addState(ps);
	}

	//
	pnodes = pnodeState->nodes("transition");
	CCARRAY_FOREACH(pnodes, pobj)
	{
		GUARD_FIELD(_new_transition);

		CAXMLNode* pnode = (CAXMLNode*)pobj;
		CATransition* pt = new CATransition();
		pt->condition = pnode->attr("condition");
		pt->tostate = pnode->attr("tostate");
		if (pnode->hasAttr("timeout"))
		{
			CAStringUtils::toFloat(pnode->attr("timeout"), pt->timeout);
		}
		this->addTransition(pt);
	}
}
